#include <iostream>
#include "sock_app.h"
#include "smtp.h"
#include "util/StringEx.h"
#include "util/Base64.h"
#include "util/File.h"
#include <sstream>

using std::cerr;
using std::cout;
using std::endl;

int main(int argc, char** argv){
    CSimpleMailSender c("smtp.163.com", 25, "xyz@163.com", "Qazqaz123.*", "zyx@163.com", "test sender", "test only.");
    //c.add_mail_attachment("/etc/a.txt");
    EMailSendResult r = c.send_mail();
    if (r == ESR_OK){
        cout << "Send mail OK" << endl;
    } else {
        cerr << "Send mail failed with error code " << r << endl;
    }
    return 0;
}

CSimpleMailSender::CSimpleMailSender(const std::string &server_domain,
                                     unsigned short server_port,
                                     const std::string &user_name,
                                     const std::string& password,
                                     const std::string &mail_send_to,
                                     const std::string &email_title,
                                     const std::string &email_content)
{
    m_server_domain = server_domain;
    m_server_port = server_port;
    m_user_name= user_name;
    m_password = password;
    m_mail_send_to = mail_send_to;
    m_email_title =email_title ;
    m_email_content = email_content;
}

CSimpleMailSender::~CSimpleMailSender()
{

}

void CSimpleMailSender::add_mail_attachment(const std::string &file_path)
{
    m_email_attachments.push_back(file_path);
}


/*
{
    send_buf = "DATA\r\n";
    send_count = send(sock_fd, send_buf.c_str(), send_buf.length(), 0);
    if (send_count != send_buf.length()){
        close(sock_fd);
        return ESR_INPUT_RCPT_TO_FAILED;
    }
    memset(recv_buf, 0, sizeof(recv_buf));
    recv_count = recv(sock_fd, recv_buf, sizeof(recv_buf)-1, 0);
    if (recv_count <= 0){
        close(sock_fd);
        return ESR_RECV_INPUT_RCPT_TO_ACK_FAILED;
    }
    if (qcutil::String(recv_buf).toUpperCase().find("ERROR:") != std::string::npos){
        close(sock_fd);
        return ESR_RECV_INPUT_RCPT_TO_ERROR;
    }
}
*/
#define SEND_CMD_TO_SERVER(DATA, EC_SEND, EC_RECV, EC_ERROR_RESULT) \
    send_count = send(sock_fd, DATA.c_str(), send_buf.length(), 0); \
    if (send_count != send_buf.length()){ \
        close(sock_fd); \
        return EC_SEND; \
    } \
    memset(recv_buf, 0, sizeof(recv_buf)); \
    recv_count = recv(sock_fd, recv_buf, sizeof(recv_buf)-1, 0); \
    if (recv_count <= 0){ \
        close(sock_fd); \
        return EC_RECV; \
    } \
    if (qcutil::String(recv_buf).toUpperCase().find("ERROR:") != std::string::npos){ \
        close(sock_fd); \
        return EC_ERROR_RESULT; \
    }


EMailSendResult CSimpleMailSender::send_mail()
{
    if (m_user_name.find('@', 0) == std::string::npos){
        return ESR_UNKNOWN_ERROR;
    }
    // 1: 创建socket连接邮件服务器
    int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    hostent* hoste = gethostbyname(m_server_domain.c_str());
    sockaddr_in server_addr;
    server_addr.sin_addr.s_addr = *(int*)hoste->h_addr_list[0];
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(m_server_port);
    int error_code = connect(sock_fd, (sockaddr*)&server_addr, sizeof(sockaddr));
    if (error_code != 0){
        return ESR_CONNECT_FAILED;
    }

    std::string send_buf;
    char recv_buf[4096] = {0};
    int send_count = -1;
    int recv_count = -1;
    // 2： Extend HELLO！
    {
        send_buf = "EHLO " + m_user_name + "\r\n";
        SEND_CMD_TO_SERVER(send_buf, ESR_SEND_EHLO_FAILED, ESR_RECV_EHLO_ACK_FAILED, ESR_RECV_EHLO_ERROR);
    }


    // 3:AUTH LOGIN
    {
        send_buf = "AUTH LOGIN\r\n";
        SEND_CMD_TO_SERVER(send_buf, ESR_AUTH_LOGIN_FAILED, ESR_RECV_AUTH_LOGIN_ACK_FAILED, ESR_RECV_AUTH_LOGIN_ERROR);
    }

    // 4: Account password base64 encode.
    {
        send_buf = m_user_name.substr(0, m_user_name.find('@', 0));
        send_buf = qcutil::Base64::encode(send_buf) + "\r\n";
        SEND_CMD_TO_SERVER(send_buf, ESR_INPUT_ACCOUNT_FAILED, ESR_RECV_INPUT_ACCOUNT_ACK_FAILED, ESR_RECV_INPUT_ACCOUNT_ERROR);
    }

    // 5: Password base64 encode.
    {
        send_buf = qcutil::Base64::encode(m_password) + "\r\n";
        SEND_CMD_TO_SERVER(send_buf, ESR_INPUT_PSWD_FAILED, ESR_RECV_INPUT_PSWD_ACK_FAILED, ESR_RECV_INPUT_PSWD_ERROR);
    }
    // 6: Mail header [Mail from:].
    {
        send_buf = "MAIL FROM: <" + m_user_name + ">\r\n";
        SEND_CMD_TO_SERVER(send_buf, ESR_INPUT_MAIL_FROM_FAILED, ESR_RECV_INPUT_MAIL_FROM_ACK_FAILED, ESR_RECV_INPUT_MAIL_FROM_ERROR);
    }

    // 7: Mail header [Mail to:]
    {
        std::istringstream iss(m_mail_send_to);
        std::string to_whom;
        while(iss >> to_whom){
            send_buf = "RCPT TO: <" + to_whom + ">\r\n";
            SEND_CMD_TO_SERVER(send_buf, ESR_INPUT_RCPT_TO_FAILED, ESR_RECV_INPUT_RCPT_TO_ACK_FAILED, ESR_RECV_INPUT_RCPT_TO_ERROR);
        }
    }
    // 8: DATA
    {
        send_buf = "DATA\r\n";
        SEND_CMD_TO_SERVER(send_buf, ESR_INPUT_DATA_FAILED, ESR_RECV_INPUT_DATA_ACK_FAILED, ESR_RECV_INPUT_DATA_ERROR);
    }

    // 9: Email head;
    {
        send_buf = "From: " + m_user_name + "\r\n";
        send_buf += "To: " + m_mail_send_to + "\r\n";
        send_buf += "Subject: " + m_email_title + "\r\n";
        send_buf += "MIME-Version: 1.0\r\n";
        send_buf += "Content-Type: multipart/mixed;boundary=qazwsxedcr\r\n\r\n";
        send_count = send(sock_fd, send_buf.c_str(), send_buf.length(), 0);
        if (send_count != send_buf.length()){
            return ESR_SEND_EMAIL_HEAD_FAILED;
        }
        // 注意上面的boundary=qazwsxedcr
    }

    // 10: Email content;
    {
        send_buf = "--qazwsxedcr\r\n";
        send_buf += "Content-Type: text/plain;";
        send_buf += "charset=\"gb2312\"\r\n\r\n";
        send_buf += m_email_content;
        send_buf += "\r\n\r\n";
        send_count = send(sock_fd, send_buf.c_str(), send_buf.length(), 0);
        if (send_count != send_buf.length()){
            return ESR_SEND_EMAIL_CONTENT_FAILED;
        }
    }

    // 11: 附件
    {
        for(auto i : m_email_attachments){
            std::string file_name = qcutil::File(i).getFileName();
            send_buf = "--qazwsxedcr\r\n";
            send_buf += "Content-Type: application/octet-stream;\r\n";
            send_buf += " name=\"";
            send_buf += file_name;
            send_buf += "\"\r\n\r\n";
            send_count = send(sock_fd, send_buf.c_str(), send_buf.length(), 0);
            if (send_count != send_buf.length()){
                return ESR_SEND_EMAIL_ATTACH_HEAD_FAILED;
            }

            qcutil::FileReader fr(i);
            if (!fr.open()){
                return ESR_READ_ATTACH_FILE_FAILED;
            }
            // @TODO: 考虑到大文件传输的问题，这里应当分段（每段后+\r\n即可）后发送。
            std::string attachment_file_content = fr.read();
            std::string send_buf = qcutil::Base64::encode(attachment_file_content);
            send_buf += "\r\n";
            send_count = send(sock_fd, send_buf.c_str(), send_buf.length(), 0);
            if (send_count != send_buf.length()){
                return ESR_SEND_EMAIL_ATTACH_CONTENT_FAILED;
            }
        }
    }
    // 12: 结束
    {
        send_buf = "--qazwsxedcr--\r\n.\r\n";
        SEND_CMD_TO_SERVER(send_buf, ESR_INPUT_FIN_FAILED, ESR_RECV_INPUT_FIN_ACK_FAILED, ESR_RECV_INPUT_FIN_ERROR);
    }
    // 13: quit
    {
        send_buf = "QUIT\r\n";
        SEND_CMD_TO_SERVER(send_buf, ESR_INPUT_QUIT_FAILED, ESR_RECV_INPUT_QUIT_ACK_FAILED, ESR_RECV_INPUT_QUIT_ERROR);
    }

    return ESR_OK;
}

